// Copyright (C) INFINI Labs & INFINI LIMITED.
//
// The INFINI Framework is offered under the GNU Affero General Public License v3.0
// and as commercial software.
//
// For commercial licensing, contact us at:
//   - Website: infinilabs.com
//   - Email: hello@infini.ltd
//
// Open Source licensed under AGPL V3:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

/* ©INFINI.LTD, All Rights Reserved.
 * mail: hello#infini.ltd */

package elastic

import (
	"fmt"
	log "github.com/cihub/seelog"
	"infini.sh/framework/core/config"
	"infini.sh/framework/core/global"
	"infini.sh/framework/core/pipeline"
	"infini.sh/framework/core/util"
	"infini.sh/framework/lib/fasthttp"
	"strings"
)

type AutoGenerateDocID struct {
	Prefix string `config:"prefix" `
}

func init() {
	pipeline.RegisterFilterPluginWithConfigMetadata("auto_generate_doc_id", New, &AutoGenerateDocID{})
}

func New(c *config.Config) (pipeline.Filter, error) {

	runner := AutoGenerateDocID{}

	if err := c.Unpack(&runner); err != nil {
		return nil, fmt.Errorf("failed to unpack the filter configuration : %s", err)
	}

	return &runner, nil
}

func (filter *AutoGenerateDocID) Name() string {
	return "auto_generate_doc_id"
}

func (filter *AutoGenerateDocID) Filter(ctx *fasthttp.RequestCtx) {

	path := string(ctx.PhantomURI().Path())
	valid, indexPath, typePath, idPath := ParseURLMeta(path)
	if global.Env().IsDebug {
		log.Tracef("auto_generate_doc_id: %v => %v, %v, %v, %v", path, valid, indexPath, typePath, idPath)
	}
	if valid {
		if idPath == "" {
			idPath = util.GetUUID()
			if filter.Prefix != "" {
				idPath = filter.Prefix + idPath
			}
			ctx.Request.Header.Set("X-Generated-ID", idPath)
			ctx.Response.Header.Set("X-Generated-ID", idPath)
		}

		uri := ctx.Request.CloneURI()
		uri.SetPath(fmt.Sprintf("/%s/%s/%s", indexPath, typePath, idPath))
		ctx.Request.SetURI(uri)
		fasthttp.ReleaseURI(uri)
	}
}

func ParseURLMeta(pathStr string) (valid bool, urlLevelIndex, urlLevelType, id string) {

	if strings.Index(pathStr, "//") >= 0 {
		pathStr = strings.ReplaceAll(pathStr, "//", "/")
	}

	if strings.LastIndex(pathStr, "/") == 0 {
		return false, urlLevelIndex, urlLevelType, id
	}

	if util.SuffixStr(pathStr, "/") {
		pathStr = util.TrimRightStr(pathStr, "/")
	}

	pathArray := strings.Split(pathStr, "/")

	last := pathArray[len(pathArray)-1]

	//only _doc and _create are valid for create new doc
	if util.PrefixStr(last, "_") && !util.ContainsAnyInArray(last, []string{"_create", "_doc"}) {
		return false, urlLevelIndex, urlLevelType, id
	}

	switch len(pathArray) {
	case 5:
		urlLevelIndex = pathArray[1]
		urlLevelType = pathArray[2]
		id = pathArray[3]
		break
	case 4:
		urlLevelIndex = pathArray[1]
		urlLevelType = pathArray[2]
		id = pathArray[3]
		break
	case 3:
		urlLevelIndex = pathArray[1]
		urlLevelType = pathArray[2]
		break
	case 2:
		urlLevelIndex = pathArray[1]
		return false, urlLevelIndex, urlLevelType, id
	}

	if util.SuffixStr(urlLevelIndex, "_") {
		return false, urlLevelIndex, urlLevelType, id
	}

	return true, urlLevelIndex, urlLevelType, id
}
